@agentorg/cli 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -11
- package/dist/bundled/index.json +4 -4
- package/dist/bundled/{sha256-87e121cbe58279f3a633e61c9773b61fc17fd53eac3d7c6f037925cbfd5d5384.agentorg → sha256-0e91cc3d0e9762b81abc2427b5496b20f383abcd6fb97d928a8d50e956b54d1f.agentorg} +0 -0
- package/dist/bundled/sha256-1f64ca62a0209d767d6aa6c4feb50b0653d60f10bebbc301f79bd3a37e0153c1.agentorg +0 -0
- package/dist/bundled/{sha256-51ecf151e590f5e57f28229d4cb36e4932a2f1670e50ddf1a3ccf335a1ee146d.agentorg → sha256-4055f8fddf6b90f3d356543f554b2a9e9a2c4a8827a8774f8f87952dc1692331.agentorg} +0 -0
- package/dist/bundled/{sha256-b76ec31282e5b863ee9569dfd9d50b283a21101cbe1b3fac23d1cbcf16b9a408.agentorg → sha256-e356ea3c1899138a8494baf521556831006f38534fa9e92ae873e7e6956c3a46.agentorg} +0 -0
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/build.d.ts +2 -2
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/build.js +14 -4
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/hub-actor.js +41 -0
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/index.d.ts +1 -1
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/index.js +1 -1
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/router.js +40 -16
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/state.d.ts +87 -2
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/state.js +312 -60
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/storage.d.ts +2 -1
- package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/storage.js +3 -2
- package/{node_modules → dist/node_modules}/@agentorg/hub/package.json +3 -0
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/hub.d.ts +62 -3
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/hub.js +54 -3
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/index.d.ts +1 -0
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/index.js +1 -0
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/installer.d.ts +37 -0
- package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/installer.js +34 -3
- package/dist/node_modules/@agentorg/interfaces/dist/src/rules.d.ts +35 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/rules.js +37 -0
- package/dist/node_modules/@agentorg/kit/dist/src/chat.d.ts +37 -0
- package/dist/node_modules/@agentorg/kit/dist/src/chat.js +131 -0
- package/dist/node_modules/@agentorg/kit/dist/src/components.d.ts +94 -0
- package/dist/node_modules/@agentorg/kit/dist/src/components.js +66 -0
- package/dist/node_modules/@agentorg/kit/dist/src/icons.d.ts +17 -0
- package/dist/node_modules/@agentorg/kit/dist/src/icons.js +30 -0
- package/dist/node_modules/@agentorg/kit/dist/src/index.d.ts +6 -0
- package/dist/node_modules/@agentorg/kit/dist/src/index.js +10 -0
- package/dist/node_modules/@agentorg/kit/dist/src/markdown.d.ts +9 -0
- package/dist/node_modules/@agentorg/kit/dist/src/markdown.js +14 -0
- package/dist/node_modules/@agentorg/kit/dist/src/resizable.d.ts +5 -0
- package/dist/node_modules/@agentorg/kit/dist/src/resizable.js +8 -0
- package/dist/node_modules/@agentorg/kit/dist/src/styles.d.ts +11 -0
- package/dist/node_modules/@agentorg/kit/dist/src/styles.js +166 -0
- package/dist/node_modules/@agentorg/kit/package.json +44 -0
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/frame/runtime.js +172 -17
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/host/runner.js +168 -11
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/client.d.ts +18 -0
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/client.js +24 -3
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-base.d.ts +8 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-base.js +64 -0
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-entry.js +4 -4
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-host.d.ts +1 -10
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-host.js +0 -23
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-protocol.d.ts +0 -8
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame.d.ts +0 -2
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame.js +0 -8
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/host.js +5 -0
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/index.d.ts +1 -1
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/module-host.d.ts +6 -1
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/module-host.js +24 -1
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/module.d.ts +30 -1
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/package.js +111 -6
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/runtime.d.ts +22 -1
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/runtime.js +114 -0
- package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/testing.d.ts +14 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/testing.js +89 -0
- package/{node_modules → dist/node_modules}/@agentorg/shell/dist/app.css +27 -144
- package/dist/node_modules/@agentorg/shell/dist/app.js +219 -0
- package/{node_modules → dist/node_modules}/@agentorg/shell/package.json +1 -0
- package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/index.d.ts +1 -1
- package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/ops.d.ts +14 -1
- package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/ops.js +22 -2
- package/dist/src/cli.js +23 -0
- package/dist/src/gateway.js +42 -1
- package/dist/src/model.d.ts +7 -4
- package/dist/src/model.js +83 -14
- package/dist/src/rules.d.ts +71 -0
- package/dist/src/rules.js +157 -0
- package/package.json +13 -16
- package/dist/bundled/sha256-e4c0e857cea3a721998effe2ca859c4011978b9bc36e63c058bfc17c27a1772e.agentorg +0 -0
- package/node_modules/@agentorg/sdk/dist/src/testing.js +0 -51
- package/node_modules/@agentorg/shell/dist/app.js +0 -9
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/hub-actor.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/logs.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/logs.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/net.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/net.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/processes.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/processes.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/router.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/server.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/server.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/validate-worker.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/validate-worker.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/validate.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/hub/dist/src/validate.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/account-login.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/account-login.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/chat.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/chat.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/http-egress.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/http-egress.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/kv.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/kv.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/layout.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/layout.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/llm-session.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/llm-session.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/storage-admin.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/storage-admin.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/ui-shell.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/ui-shell.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/ui-view.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/dist/src/ui-view.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/interfaces/package.json +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/build.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/build.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/canonical.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/canonical.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/cli.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/cli.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/client-http.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/client-http.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/describe.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/describe.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/errors.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/errors.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-entry.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/frame-protocol.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/host-main.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/host-main.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/host.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/index.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/interface.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/interface.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/manifest-api.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/manifest-api.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/manifest.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/manifest.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/module.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/package.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/person-commands.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/person-commands.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/tar.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/dist/src/tar.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/sdk/package.json +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/shell/dist/agent-bindings.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/shell/dist/index.html +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/shell/src/agent-bindings.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/bytes.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/bytes.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/frames.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/frames.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/index.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/transports.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/transports.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/yamux.d.ts +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/dist/src/yamux.js +0 -0
- /package/{node_modules → dist/node_modules}/@agentorg/wire/package.json +0 -0
package/README.md
CHANGED
|
@@ -12,14 +12,15 @@ AgentOrg as a dynamic system: Actors and widgets are modules in content-addresse
|
|
|
12
12
|
|
|
13
13
|
## Packages
|
|
14
14
|
|
|
15
|
-
- `@agentorg/sdk`: `defineInterface` (with `method` and `event`) for `rpc`, `stream` and `http` interfaces; `defineActor` for host modules and `defineWidget` for view modules, with typed slot and export handles; the errors `NotConnected`, `Refused` and `OutcomeUnknown`; and `createTestContext` (`@agentorg/sdk/testing`), which runs a module without a Hub. An `rpc` handler gets its input and `{ caller }`, the calling Actor as the Hub names it, and `emit(event, payload, { to })` sends an event only to the subscriptions of the Actor `to` names, so a provider serving several consumers keeps each one's events its own. In `createTestContext`, `call` takes the caller as a fourth argument (the module's own Actor by default) and `emitted` records each event's `to`. `@agentorg/sdk/client` connects a program outside the Hub as an Actor: `connect({hubUrl, actorId, credential, slots, exports})` gives the same handles as a module's context, on the same runtime, with slots bound as the Hub's notices say and `exports.add`/`withdraw()` over the channel; As in a module, implement in `start` (run before the channel opens) and use slots from `onBind`, which runs when a slot becomes usable: bound, or its provider back. `openChannel` is the wire layer beneath it, without local checks. `http` slots `fetch` and `http` exports `serve` over the Hub's stream (HTTP/1.1, one request per stream, `node:http` loaded only when used). The request line carries the consumer's absolute URL, so a handler's `request.url` is exactly the URL the consumer fetched, scheme included; a request line that isn't absolute, or whose host isn't the `Host` header's, is answered 400. A handler that throws `Refused`, `NotConnected` or `OutcomeUnknown` reaches the consumer's `fetch` as that same error (carried as a 502 with an `agentorg-error` header); any other throw is a plain 500 response. `@agentorg/sdk/host` runs a package Actor's host modules in one process, on the same runtime: `runHost({packageDir, actorId, channel, log})` imports each `host` module's bundle, runs every `start`, then says hello over the channel, with each module's slots and exports named `<module>.<name>`. The program the Hub runs is `@agentorg/sdk/host/runner.js`, one self-contained file: it takes `<packageDir> <actorId>`, uses fd 3 as the channel, writes `ctx.log` as JSON lines on stderr, and exits non-zero if a `start` throws or an exception goes uncaught. The SDK's errors keep their class and code between a package's bundle and the runner, though each carries its own copy of the SDK. It reads only itself and the package's folder, so it runs under Node's permission model with exactly those two paths allowed (as real paths: the allow list doesn't follow symlinks).
|
|
15
|
+
- `@agentorg/sdk`: `defineInterface` (with `method` and `event`) for `rpc`, `stream` and `http` interfaces; `defineActor` for host modules and `defineWidget` for view modules, with typed slot and export handles; the errors `NotConnected`, `Refused` and `OutcomeUnknown`; and `createTestContext` (`@agentorg/sdk/testing`), which runs a module without a Hub. An `rpc` handler gets its input and `{ caller }`, the calling Actor as the Hub names it, and `emit(event, payload, { to })` sends an event only to the subscriptions of the Actor `to` names, so a provider serving several consumers keeps each one's events its own. In `createTestContext`, `call` takes the caller as a fourth argument (the module's own Actor by default) and `emitted` records each event's `to`. `@agentorg/sdk/client` connects a program outside the Hub as an Actor: `connect({hubUrl, actorId, credential, slots, exports})` gives the same handles as a module's context, on the same runtime, with slots bound as the Hub's notices say and `exports.add`/`withdraw()` over the channel; As in a module, implement in `start` (run before the channel opens) and use slots from `onBind`, which runs when a slot becomes usable: bound, or its provider back. `openChannel` is the wire layer beneath it, without local checks. `http` slots `fetch` and `http` exports `serve` over the Hub's stream (HTTP/1.1, one request per stream, `node:http` loaded only when used). The request line carries the consumer's absolute URL, so a handler's `request.url` is exactly the URL the consumer fetched, scheme included; a request line that isn't absolute, or whose host isn't the `Host` header's, is answered 400. A handler that throws `Refused`, `NotConnected` or `OutcomeUnknown` reaches the consumer's `fetch` as that same error (carried as a 502 with an `agentorg-error` header); any other throw is a plain 500 response. `@agentorg/sdk/host` runs a package Actor's host modules in one process, on the same runtime: `runHost({packageDir, actorId, channel, log})` imports each `host` module's bundle, runs every `start`, then says hello over the channel, with each module's slots and exports named `<module>.<name>`. The program the Hub runs is `@agentorg/sdk/host/runner.js`, one self-contained file: it takes `<packageDir> <actorId>`, uses fd 3 as the channel, writes `ctx.log` as JSON lines on stderr, and exits non-zero if a `start` throws or an exception goes uncaught. The SDK's errors keep their class and code between a package's bundle and the runner, though each carries its own copy of the SDK. It reads only itself and the package's folder, so it runs under Node's permission model with exactly those two paths allowed (as real paths: the allow list doesn't follow symlinks). A slot made at run time, another Actor's export exposed to this Actor rather than declared by its package, isn't in `ctx.slots`: `ctx.exposed()` lists those bound now, each as `{slot, described, provider: {actor, export}, call, on}`, with `described` the export's JSON Schemas and `call` one function per method, its values checked by the Hub rather than locally; `ctx.onExposed(fn)` runs after one comes or goes. Every host module of the Actor sees them, under the Actor's slot name; a view module sees none. The Hub's `slot` notice carries such a slot's `exposed` (the export's described interface and its provider) while it is bound, one notice each, including right after hello, which names them among `bound`; `createTestContext` fakes one with `expose(slot, iface, handlers)` and `unexpose(slot)`.
|
|
16
16
|
- `@agentorg/interfaces`: the standard interfaces `kv@1`, `ui.view@1`, `chat@1` (a conversation with an agent; a `tool` message may carry the call it stands for, its name, `running`/`done`/`refused`, input and output, sent as `running` when it starts and again under the same id when it ends), `llm.session@1` (stateless model inference, with function tools), `account.login@1` (signing in to an outside account, driven by a person or a program), `http.egress@1` (HTTPS to the public internet), `storage.admin@1` (seeding a new Actor's empty stores), `layout@1` (the person's dashboard), `installer@1` (planning and installing a package) and `ui.shell@1` (what the shell offers a view in its frame: opening an https address and starting sign-in), and the Hub's management interfaces `hub.packages@1`, `hub.actors@1`, `hub.grants@1`, `hub.bindings@1`, `hub.exports@1` and `hub.audit@1`.
|
|
17
|
+
- `@agentorg/kit`: the shell's own components, for the shell and for any view, drawn in the theme's tokens by the kit's styles (`KIT_CSS`, every class `kit-`, put first in the document's head when the kit is imported, so a view's own styles win). Accessible primitives on Radix: `Dialog` (focus held inside; Escape and a click outside close it), `Menu` with `MenuItem`, `Switch` and `Tabs`; plain elements for the rest: `Button` (`primary`, `quiet`), `IconButton` (its label is its name), `Field`, `Select` (the browser's own), `Card`, `List`, `Table`, `Meter` and `Actions`; layout pieces that fill and adapt: `Stack`, `Row`, `Fill`, `Scroll`, and `ResizableGroup`, `ResizablePanel` and `ResizeHandle` from react-resizable-panels. `Markdown` renders an agent's GitHub-flavored markdown with react-markdown, safe as it streams: remend first closes what's still open, and raw HTML is shown as text. `Chat` is a conversation on assistant-ui's primitives over an external store: the caller keeps the messages (`ChatEntry`, as `chat@1` has them) and runs the turns (`onSend`, `onCancel`, `onRetry`, `onEarlier`). The person's messages, each run of replies and tool messages as one reply in markdown, a tool message as a card (its name, how it ended, what went in and came back) when it says which tool ran and as its line otherwise; Enter sends, never mid-composition or during a turn, when Send is Stop; a refused send puts the text back; it fills its parent and goes compact below 460 px wide. Its tests run on happy-dom.
|
|
17
18
|
- `@agentorg/wire`: the channel: yamux streams, length-prefixed JSON frames, stream headers and transports.
|
|
18
|
-
- `installer`: a bundled host package that turns packages into working Actors, exporting `installer@1`. Bound to the Hub's management (`hub.packages`, `hub.actors`, `hub.grants`, `hub.exports` and `hub.bindings`, which makes it the person's delegated manager) and to `storage.admin@1`, it proposes and installs. `plan({package})` lists each slot the package doesn't wire itself, with the exports that could serve it now: exactly its interface and schema hash, on Actors that aren't stopped or closed. `install({package, name, connections, start?})` makes the Actor with exactly those connections, all or none. It grants nothing its caller couldn't: each connection's export side must be the caller's (the provider, bound to that export, or a manager), else the whole install is refused (`forbidden`, listing every such connection) and nothing is made. It seeds each store bound to `storage.kv` from the package's `seed` (an object of key to value per `module.slot`), starts the Actor if asked (a seed or start that fails closes it), and lists the slots left unbound and the seeds that couldn't land (`unseeded`). It never picks a connection itself. Bound to the person's `layout@1` too, it carries a system between homes as data: `exportSystem()`, for a manager only (it lists every binding and the person's layout; any other caller is refused `forbidden`), gives an `agentorg.system/1` composition (every package Actor that isn't closed, by a name of its own, with its package as `name@version#hash` and whether it runs; the connections among them and to the built-in Actors, as `{consumer, slot, provider, export}` by names; and the layout's placements), never credentials, tokens, stores or logs, and lists what it leaves out (external Actors and what connects to them). `importSystem({composition})` makes it again with the caller's authority (connecting the new Actors to each other takes a manager): it creates the Actors whose packages are here (a taken name gets a suffix), binds the connections, seeds, starts what ran and places the layout, all or nothing, and lists what it couldn't: a package that isn't here or is another version (whoever holds the composition's folder adds its archives first) and what depends on it, an Actor that needs a sign-in, a seed that can't land, and the placements unless the caller holds the person's layout. The exporting home's installer stays out: every home has its own. An import never grants management: a connection to `hub` or to `storage.admin` is recorded by an export but never made by an import, even a manager's (a composition may come from anyone); it comes back in `ungranted`, for the person to grant one by one.
|
|
19
|
-
- `@agentorg/hub`: the core. Its state, in `node:sqlite` in a private home: Actors, a verified package store (`hub.packages.add` takes an archive by path; `hub.packages.build` takes a package's source as data, `package.json` and files under `src/` up to 768 KiB, builds it as `agentorg build` does, but reading no tsconfig, in a scratch folder of the home that is always removed: its files import only `@agentorg/sdk`, `@agentorg/interfaces` and `
|
|
19
|
+
- `installer`: a bundled host package that turns packages into working Actors, exporting `installer@1`. Bound to the Hub's management (`hub.packages`, `hub.actors`, `hub.grants`, `hub.exports` and `hub.bindings`, which makes it the person's delegated manager) and to `storage.admin@1`, it proposes and installs. `plan({package})` lists each slot the package doesn't wire itself, with the exports that could serve it now: exactly its interface and schema hash, on Actors that aren't stopped or closed. `install({package, name, connections, start?})` makes the Actor with exactly those connections, all or none. It grants nothing its caller couldn't: each connection's export side must be the caller's (the provider, bound to that export, or a manager), else the whole install is refused (`forbidden`, listing every such connection) and nothing is made. It seeds each store bound to `storage.kv` from the package's `seed` (an object of key to value per `module.slot`), starts the Actor if asked (a seed or start that fails closes it), and lists the slots left unbound and the seeds that couldn't land (`unseeded`). It never picks a connection itself. `install` records the new Actor as made for its caller (`installedFor`, which grants nothing), and takes `expose: [{export, to}]`: the new Actor's exports to expose, each to one Actor, all or none like the connections. A manager's are made at once (`granted`, with the slot); one to the caller itself is left for the caller to file, its result giving the exact `hub.grants.expose` call, which makes it the caller's own request (`request`); any other is refused `forbidden`, saying a manager could, and nothing is created. Only `rpc` exports of the package's host modules can be listed, and the Hub's own Actors take no slots. `plan({package, expose})` lists each entry with how it would settle, and refuses one `install` would refuse. Nothing is exposed that isn't listed. Bound to the person's `layout@1` too, it carries a system between homes as data: `exportSystem()`, for a manager only (it lists every binding and the person's layout; any other caller is refused `forbidden`), gives an `agentorg.system/1` composition (every package Actor that isn't closed, by a name of its own, with its package as `name@version#hash` and whether it runs; the connections among them and to the built-in Actors, as `{consumer, slot, provider, export}` by names; and the layout's placements), never credentials, tokens, stores or logs, and lists what it leaves out (external Actors and what connects to them). `importSystem({composition})` makes it again with the caller's authority (connecting the new Actors to each other takes a manager): it creates the Actors whose packages are here (a taken name gets a suffix), binds the connections, seeds, starts what ran and places the layout, all or nothing, and lists what it couldn't: a package that isn't here or is another version (whoever holds the composition's folder adds its archives first) and what depends on it, an Actor that needs a sign-in, a seed that can't land, and the placements unless the caller holds the person's layout. The exporting home's installer stays out: every home has its own. An import never grants management: a connection to `hub` or to `storage.admin` is recorded by an export but never made by an import, even a manager's (a composition may come from anyone); it comes back in `ungranted`, for the person to grant one by one.
|
|
20
|
+
- `@agentorg/hub`: the core. Its state, in `node:sqlite` in a private home: Actors, a verified package store (`hub.packages.add` takes an archive by path; `hub.packages.build` takes a package's source as data, `package.json` and files under `src/` up to 768 KiB, builds it as `agentorg build` does, but reading no tsconfig, in a scratch folder of the home that is always removed: its files import only `@agentorg/sdk`, `@agentorg/interfaces`, `zod`, and for views `react`, `react-dom` and `@agentorg/kit` (JSX compiles to `react/jsx-runtime`), by name, each only within its own package, nothing outside the package's folder, and nothing by a pattern; a library's own imports are its own, and only the files resolved for it are read; a library's wrapped modules are named in the bundle by their library and version (`agentorg-library:react@19.3.1/cjs/react.production.js`), never by where it is installed, in `agentorg build` and Hub builds alike, so a package names no machine's folders and builds the same on any install; a Hub build resolves each allowed package's entry file from the Hub's own install, so in that file esbuild omits Node's ESM-to-CommonJS interop flag, which `agentorg build` keeps: no package allowed now behaves differently for it, but a Hub build's bytes can differ from `agentorg build`'s for packages that use them; at most 16 modules, 16 MiB built and 60 s; one build per caller at a time; and it adds the result. Reading each module's definition runs the source's code, so the Hub runs it in the same OS sandbox as host processes, and where those can't start, `build` is refused `unsupported` with the same reason. A failed build is `Refused("build")` with the build's message, which names no path outside the package), what each Actor takes and offers, bindings, binding requests and an audit trail. A binding is the grant, and it needs both ends: the export side (the provider, an Actor already bound to that export, or a manager) and the slot side (the consumer itself, or a manager). Through `hub.grants@1`, a caller with both ends binds; one with a single end approves the other end's open request or asks that end's approvers; one with neither is refused. `hub.grants.expose({provider, export, to, as?})` exposes an `rpc` export to one Actor: a slot made on `to` at run time, typed by the export's interface and bound to it in one step, named `as` (one word of at most 48 characters) or by default the provider's name camelCased, `_` and the export's last part (`Pocket Side Quests`' `logic.quests` is `pocketSideQuests_quests`), made unique on `to` with `_2`, `_3`…. Its authority is a binding's, `to` being the consumer: with both ends it is made; with one it is a request (`exposed: true` on the request, its `slot` the name it will have), which the other end approves by calling `expose` with the request's fields; with neither it is refused, naming who could. Exposing the same export to the same Actor again gives its slot back. A request reserves no name: the first exposure made under it has it, and the requests it settles close; one Actor has at most 32 exposure requests open toward another. An Actor that declares a slot of its own under an exposed slot's name takes the name, and the exposure goes. The slot goes with its binding: `unexpose({to, slot})`, `unbind`, the export withdrawn or either end closed; `hub.bindings` marks such a binding `exposed: true`. The Hub's own Actors take no slots. A manager creating an Actor for another (the installer, for its caller) passes `for`, shown as `installedFor` on the Actor: provenance only, from which the Hub grants nothing. A decision goes to the request's approvers and its requester, which can also read its requests' outcomes with `mine`. A manager is any Actor bound to `hub.bindings@1`. `hub.actors.create` can bind the new Actor's slots at once, with the creator's export side. The router serves it over WebSocket (`listen`), one yamux channel per Actor: a client's first stream is `hello` with its credential, then every `call`, `subscribe`, `open` and `http` stream is resolved through its binding, validated against the interface's JSON Schemas in both directions (ajv, 2020-12, in a pool of worker threads, each check under a time budget, since schemas come from Actors), forwarded to the provider with the caller named, and audited. A provider accepts or refuses an `open` or `http` stream with one body before any raw bytes. An Actor offers and withdraws exports at run time on its own channel (`offer`, `withdraw`). An Actor learns which of its slots are bound from hello's result, then from a `slot` notice on each change. A pipe the Hub makes for a package Actor's process says hello as that Actor without a credential (the pipe is the proof) and declares nothing (the manifest does). Every `serve` stream names the caller and the caller's slot, so a provider can keep state per consumer slot. `hub.actors.start` runs a package Actor's host modules in their own process: `node` under its permission model (reading only the runner and the package's folder, no child processes or workers), with an empty environment, a 256 MB heap and the channel on fd 3, inside the OS sandbox (`sandbox-exec` on macOS, a user, network and PID namespace with `unshare` on Linux), so it has no network, no DNS and no other process to signal. The Actor is running once its process says hello, within 30 s. `stop` sends SIGTERM, then SIGKILL after 5 s. A process that exits otherwise has crashed: it is restarted after a backoff from 1 s doubling to 60 s (starting over after 10 minutes up), and `hub.actors.get` counts `restarts` and keeps the `lastExit`. Every Actor's record in `hub.actors` says whether it is `connected`, holding a channel to the Hub now: an external Actor while its program is connected (the person while the app runs: its gateway holds the person's channel), a package Actor while its process is, so a running one isn't while its process starts or restarts; the built-in Actors are the Hub itself and have none. `hub.actors`' `changed` event names an Actor whose record may differ: made, started, stopped, closed, its process exited, or connected or gone; the audit trail records no connections. Actors running when the Hub stopped are started again when it opens. `hub.actors.upgrade({actorId, package})` runs another package as the same package Actor, for a manager only, since the new package's code then holds everything the Actor was granted: its id, its stores and every binding whose interfaces still match stay, the new package's own wiring is made where its slot is unbound, and a running Actor restarts on it; a binding through a slot or export the new package drops or declares as another interface is dropped, audited and returned in `dropped`. When a redeclaration or an upgrade changes the interface of a bound name, the bindings whose other end doesn't declare the new one are dropped (audited as `binding.drop`) and the requests through it close: an external Actor's hello that redeclares a bound name as another is no longer refused for it. Dropping only takes access away. Where the sandbox can't run (stock Ubuntu restricts unprivileged user namespaces), `start` is refused with `unsupported` and every package Actor's `host` says why and what to change; the Hub probes again at each start. `hub.actors.logs` reads an Actor's log lines: its `ctx.log` entries and anything else its process writes (stdout as `info`, stderr as `error`). For the person's shell, `Hub.viewCall` and `Hub.viewSubscribe` run a package Actor's view module's calls and events in the Hub's process, as that Actor, through `<module>.<slot>`, and `Hub.viewSlots` tells the gateway a view module's slots: those bound now, then each change, and each bound again when it is usable again: its provider's channel comes back, or its Actor's own, since a view calls only while its Actor runs; `hub.actors.get` lists a package Actor's `views`. `Hub.viewLog` writes a view module's log line to its Actor's log, marked with the module; the Hub bounds each line itself (a known level, 4 KiB of text, 16 fields of 1 KiB) and rotates each Actor's log once at 1 MiB. The built-in `storage` Actor exports `kv@1`: one store per consumer slot (keyed by the consumer and slot the router names), in `<home>/storage.db`; values up to 256 KiB and 64 MiB per Actor, past which `Refused("quota")`; stores outlive unbinding and go when their Actor is closed. Its `admin` export (`storage.admin@1`, bound only to the installer) seeds a new Actor's store from its package's `seed`: all entries or none, into an empty store only, within the same limits, and audited as `storage.seed` with the consumer, the slot and how many entries. The built-in `net` Actor exports `egress` (`http.egress@1`): a consumer's request, made from the Hub's process to the public internet over HTTPS only. Its host is resolved and every address checked (loopback, private, shared, link-local including the cloud metadata address, multicast, reserved and documentation ranges, and their IPv4-mapped forms are refused), and the connection goes to the checked address with the name for SNI and the certificate, so a second resolution can't move it. Plain `http:`, a refused address and a name that resolves to one throw `Refused("forbidden")` at the consumer's `fetch`; a host that can't be reached before anything is sent throws `Refused("unreachable")`, and a failure after, or 5 minutes without a byte, throws `OutcomeUnknown`. Redirects come back as they are. Each request's method, host and status are audited as `net`'s; paths, queries and bodies never are. The `hub` Actor's six exports are served through the same path. A refusal that names an Actor names it as "Name (id)", and says what the caller can do instead: who could grant it, what to call, or what to do first.
|
|
20
21
|
- `@agentorg/cli`: AgentOrg as one app. `agentorg` runs a Hub, the person's gateway and the shell on loopback; see "The app: the person's gateway".
|
|
21
22
|
- `chatgpt`: the model Actor, a package with one host module. It signs in with ChatGPT for the person's plan (`account.login@1`: OpenAI's OAuth for open-source apps, with dynamic registration, PKCE and a verified id_token; refresh tokens rotate, one refresh at a time) and serves `llm.session@1` on that plan through the Responses API, streamed and stored nowhere. Its whole network is its `web: http.egress@1` slot and its sign-in lives in its `store: kv@1` slot; the tokens never leave it except to that store: never in a reply, an event, a log or a refusal. It never uses an API key or API billing: a usage limit is refused `usage_limit`, with where to manage it. Its tests run against an in-process fake of OpenAI, so none reaches OpenAI.
|
|
22
|
-
- `assistant`: an agent, a package whose host module `logic` exports `chat@1` and runs the agent loop itself, with the model behind its `model: llm.session@1` slot. Its tools are its other bound slots (`layout`, `packages`, `actors`, `grants`, `exports` and `installer`): each method becomes a function tool named `<slot>__<method>` with the method's input JSON Schema, so it can do exactly what it is connected to, and a call runs through the slot, checked and audited by the Hub. With `packages` and `installer: installer@1` bound, it can extend the system: its instructions then give the package format, with a whole counter package as the
|
|
23
|
+
- `assistant`: an agent, a package whose host module `logic` exports `chat@1` and runs the agent loop itself, with the model behind its `model: llm.session@1` slot. Its tools are its other bound slots (`layout`, `packages`, `actors`, `grants`, `exports` and `installer`), and every slot exposed to it at run time (`ctx.exposed()`): each method becomes a function tool named `<slot>__<method>` (a dot in the slot's name as `_`; a name past 64 characters is left out) with the method's input JSON Schema, so it can do exactly what it is connected to, and a call runs through the slot, checked and audited by the Hub. With `packages` and `installer: installer@1` bound, it can extend the system: its instructions then give the package format (views may use React and `@agentorg/kit`'s components), with a whole counter package and a view on the kit as the examples (`EXAMPLE` in `src/logic.ts`, built by its tests as the Hub builds), and the steps: `packages__build` with the files, fixing errors from the build's message; `installer__plan`; `installer__install` with explicit connections (a store to `storage.kv`); then `layout__place` with the new Actor's view. A turn makes at most 20 tool calls, and a function's output is cut at 16 KiB. Its chat is kept in its `store: kv@1` slot: each message once, its text and a call's arguments cut to fit a stored value, and the newest 2,000 messages (16 MiB) kept. The model is sent the newest whole exchanges that fit 400 KB of JSON (instructions and tool schemas aside), and always the latest one; if that alone is over, its oldest function outputs are left out, each call keeping an output. Its view module `chat` is the chat itself, the kit's `Chat` over its `chat: chat@1` slot: the record (the newest 100, earlier pages on request), the replies as they stream, in markdown, and a composer, with "Using ChatGPT plan" and "Manage usage" beside it, Retry under the last reply, and the action each refusal calls for (Manage usage, Continue with ChatGPT, Retry), which it asks of the shell through its `shell: ui.shell@1` slot.
|
|
23
24
|
|
|
24
25
|
### Why `ui.view@1` is different
|
|
25
26
|
|
|
@@ -34,9 +35,9 @@ A shell runs each view module in its own sandboxed frame, with an opaque origin
|
|
|
34
35
|
- **`ui.view@1`:** the shell drives `mount`, `resize` and `unmount`. At mount, the theme's CSS variables (names starting `--`) are set on the frame's document.
|
|
35
36
|
- **`ui.shell@1`:** the shell's own interface, the mirror of `ui.view@1`. A view's slot of it is bound by the frame runtime itself, never by the Hub, so it is always bound. `openExternal({url})` takes only an `https:` URL, which the host checks; the shell's handler must open it in a new tab with no opener (`noopener`). `signIn()` starts the shell's own sign-in. Each method has one call in flight at a time. A frame can't open windows itself.
|
|
36
37
|
- **Logs:** the view's `ctx.log` and any error it doesn't catch go to the shell, for its Actor's log.
|
|
37
|
-
- **
|
|
38
|
+
- **Size and base styles:** the placement decides a frame's size, never its content: the view learns it from `mount` and `resize`. Before the view loads, the frame runtime puts a base stylesheet first in the document (`FRAME_BASE`): the document and body fill the frame on a transparent background, content that grows scrolls inside with no visible scrollbar, and plain HTML (text, headings, lists, buttons, inputs, checkboxes, tables, code) is drawn in the theme's tokens. Every rule is in `:where()`, so any rule the view writes outranks it.
|
|
38
39
|
|
|
39
|
-
The frame isn't trusted: `FrameHost` checks every message, drops what doesn't fit, and refuses what it can't serve. A frame holds one subscription per slot and event, 64 at most, and 64 calls in flight, past which a call is refused `busy`; its log lines are cut, their fields rebuilt as a small fresh object, and past 50 lines in 10 s the rest are dropped.
|
|
40
|
+
The frame isn't trusted: `FrameHost` checks every message, drops what doesn't fit, and refuses what it can't serve. A frame holds one subscription per slot and event, 64 at most, and 64 calls in flight, past which a call is refused `busy`; its log lines are cut, their fields rebuilt as a small fresh object, and past 50 lines in 10 s the rest are dropped.
|
|
40
41
|
|
|
41
42
|
## Building a package
|
|
42
43
|
|
|
@@ -54,11 +55,11 @@ A package's `package.json` names its modules, its own wiring and any seed state:
|
|
|
54
55
|
|
|
55
56
|
`agentorg build [dir]` (from `@agentorg/sdk`; `buildPackage` in `@agentorg/sdk/build`) then:
|
|
56
57
|
|
|
57
|
-
- bundles each module, with every library it uses, into one ESM file, `dist/<module>.js`, with no imports at run time and no Node built-ins
|
|
58
|
+
- bundles each module, with every library it uses, into one ESM file, `dist/<module>.js`, with no imports at run time and no Node built-ins. JSX uses the automatic runtime (no file needs React in scope), and `process.env.NODE_ENV` is `"production"`, so a view built with React and the kit runs in a frame, which has no `process`;
|
|
58
59
|
- reads each module's definition from its code and writes `agentorg.json`: each module's placement, slots and exports, every interface's JSON Schemas and hash, the package's `connections` and `seed`;
|
|
59
60
|
- hashes the tree, `agentorg.json` and `dist/`, as SHA-256 over the canonical list of each file's path and SHA-256, and archives it as `<hash>.agentorg`, a deterministic tar. The tree goes to `agentorg-build/package/` and the archive beside it.
|
|
60
61
|
|
|
61
|
-
It finds `@agentorg/sdk`, `@agentorg/interfaces` and `zod` from the package's own folder, so it works in a checkout of this repository, where they are. With `agentorg` installed from npm, build through the running app instead: `agentorg call packages build '{"files": {"package.json": "…", "src/logic.ts": "…"}}'` is the Hub's build (`hub.packages.build`), which takes the source as data, builds it as above against the SDK, the interfaces and
|
|
62
|
+
It finds `@agentorg/sdk`, `@agentorg/interfaces` and `zod` from the package's own folder, so it works in a checkout of this repository, where they are. With `agentorg` installed from npm, build through the running app instead: `agentorg call packages build '{"files": {"package.json": "…", "src/logic.ts": "…"}}'` is the Hub's build (`hub.packages.build`), which takes the source as data, builds it as above against the SDK, the interfaces, zod, React, react-dom and the kit it ships with, and adds the package.
|
|
62
63
|
|
|
63
64
|
A signature is detached: a file named `agentorg.sig` beside the package, signing the package hash, so it is never inside what it signs. The manifest has no signature field.
|
|
64
65
|
|
|
@@ -85,6 +86,7 @@ To read a module's definition, the build runs the module's code in a separate No
|
|
|
85
86
|
`@agentorg/cli` provides `agentorg`.
|
|
86
87
|
|
|
87
88
|
- **`agentorg`**, with no command, runs AgentOrg: a Hub in its home, the person's gateway and the shell, on `127.0.0.1` (`--port N`, else any free port). It opens the browser already signed in, and prints only its address, never a sign-in code. `--no-open` doesn't open a browser. The code is in the address it hands the system's opener; on Linux, other local users can see that program's arguments for the moment it runs, and the code works for 60 s.
|
|
89
|
+
- **`agentorg --version`** prints the version. An option the command doesn't know is refused with the usage (exit 2); it never starts the app.
|
|
88
90
|
- **`agentorg open`** signs a browser in to the running app again.
|
|
89
91
|
- **`agentorg build [dir]`** builds a package, as the SDK's build does, in a checkout of this repository. Installed from npm, build with `agentorg call packages build '{"files": {…}}'`, the Hub's build.
|
|
90
92
|
- **`agentorg hub [--port N]`** runs a bare Hub in its home, without the gateway or the shell, serving WebSocket on loopback, and writes the address to `<home>/url`.
|
|
@@ -135,8 +137,10 @@ From a clone, after `npm ci` and `npm run build`, with `agentorg` being `node pa
|
|
|
135
137
|
- it makes the installer as the person, with its seven bindings (`hub.packages`, `hub.actors`, `hub.grants`, `hub.exports`, `hub.bindings`, `storage.admin` and `person.layout`), starts it, and binds the person's `installer` to it;
|
|
136
138
|
- once the installer's process is connected, it installs ChatGPT through `installer.install`, with `model.web` to `net.egress` and `model.store` to `storage.kv`, started, and binds the person's `login` to its `model.login`.
|
|
137
139
|
|
|
138
|
-
It records the Actors it made, and the bundled assistant's hash, in the person's store (`app/first-run`), and trusts only those, never an Actor because of the name its package gives itself. Each step reads what is there first, so a restart, or a first run that stopped half way, converges and makes nothing twice: a recorded Actor that lost one of its slots' bindings gets it again (of the slots its package declares)
|
|
140
|
+
It records the Actors it made, and the bundled assistant's hash, in the person's store (`app/first-run`), and trusts only those, never an Actor because of the name its package gives itself. Each step reads what is there first, so a restart, or a first run that stopped half way, converges and makes nothing twice: a recorded Actor that lost one of its slots' bindings gets it again (of the slots its package declares). A recorded Actor of an older build is upgraded in place to the bundled one (`hub.actors.upgrade`: the same id, store and matching bindings), and so is an agent on an assistant build an earlier run bundled, known by the hashes the first run records, never by a package's name; the bindings an upgrade drops are made again where their ends now match, and each upgrade is logged. A recorded Actor that was closed is made again. A step that fails is logged and tried again at the next start.
|
|
139
141
|
- **`/oauth/chatgpt`:** where ChatGPT sends the browser back after sign-in. It takes `GET` only and hands the whole address, which holds the sign-in's code, to `login.complete`. Then it sends the tab on (303) to `/oauth/chatgpt?result=<outcome>`, so the code leaves the tab's address and history; that page says how it went and links back to the shell. The code is never logged or shown, and no referrer is sent.
|
|
142
|
+
- **Named rules, `app.rules@1`:** conveniences the person turns on, served by the gateway on `/api` as `rules` (`list`, `set`, and `changed`). Each is named and off until the person turns it on, and the ones that are on are kept in the person's store under `app/rules`. The gateway acts on a rule that is on as the person, a manager, so only within what the person could grant: it hears each request through `grants.requested`, and goes through the open ones at start and whenever a rule is turned on. It approves a match the way an approver does, with the call the request asked for, and never declines; whatever no rule matches stays for the person.
|
|
143
|
+
- **"Let an agent use what it installs"** (`agents-use-what-they-install`) approves a request to expose an Actor's export to the agent it was installed for (its `installedFor`), while the Actor isn't closed. `installedFor` is provenance only: nothing but a rule that is on acts on it.
|
|
140
144
|
- **`layout@1`:** the person exports it, so a program bound to it places a view as the person does. Each placement records `by`, the Actor that placed it, from the call's caller as the Hub names it (the person, through `/api`); no input sets it. The layout is kept in the person's `store` slot (`kv@1`) under the key `layout`, and the app binds that slot to the built-in `storage` where there is one.
|
|
141
145
|
- `place` takes only a `view` module of a package Actor, with no `after` meaning last.
|
|
142
146
|
- `move` with no `after` means first.
|
|
@@ -150,8 +154,8 @@ From a clone, after `npm ci` and `npm run build`, with `agentorg` being `node pa
|
|
|
150
154
|
- The person's `installer` slot, bound by the app's first run, serves both.
|
|
151
155
|
- **`/frames/`:** a view's frame, its runtime and its bundle, for `view` modules only, and only by package hash. Every response's policy is `sandbox allow-scripts`, so whatever loads there runs on an opaque origin, even opened as a top page, and never as the shell's origin, where the session is. It allows scripts only from the gateway's `/frames/`, inline styles, and no fetches, forms or windows. The frame's origin is opaque, so its module scripts are CORS requests from `Origin: null`: only those are answered `Access-Control-Allow-Origin: null`, and nothing there reads or needs the session.
|
|
152
156
|
- **The app's own pages** carry a policy of their own: scripts and styles from the app only, framed by nothing.
|
|
153
|
-
- **The shell,** `@agentorg/shell`, is what the gateway serves at `/`: one socket to `/api`, and nothing it decides on its own.
|
|
154
|
-
- **Agents** are the package Actors with a `chat` view. Selecting one mounts that view in the main container, with the placements beside it whose `actor` is that agent or whose `by` is. Between them, a handle (dragged, or the arrow keys) sets that column's width, from 280 to 640 px and at most half the stage, kept in this browser; an agent with nothing beside it has no column, and its chat takes the width. A placed view, beside an agent
|
|
157
|
+
- **The shell,** `@agentorg/shell`, is what the gateway serves at `/`: one socket to `/api`, and nothing it decides on its own. It is built on `@agentorg/kit`: its dialogs, menus, switches, tabs, icons and the column's handle are the kit's.
|
|
158
|
+
- **Agents** are the package Actors with a `chat` view. Selecting one mounts that view in the main container, with the placements beside it whose `actor` is that agent or whose `by` is. Between them, a handle (dragged, or the arrow keys) sets that column's width, from 280 to 640 px and at most half the stage, kept in this browser; an agent with nothing beside it has no column, and its chat takes the width. A placed view's frame fills its placement, whatever it draws: beside an agent, the column's height is shared among its placements, each at least 220 px (the column scrolls past that), and on Home each placement is 300 px high. The handle is react-resizable-panels', through the kit; while it's dragged, frames take no pointer events, so a drag over a view keeps going.
|
|
155
159
|
- **Home** shows the open grant requests, with Allow and Decline, then every placement of `layout@1`, in order. Each event (`layout` `changed`, `grants` `requested` and `decided`, `actors` `changed`, and the audit's package and binding entries) reads its part again. A subscription the gateway ends is made again after a wait (1 s, growing to 30 s while it keeps ending), and what it says is read again then; a view's own subscriptions end in its frame instead.
|
|
156
160
|
- **Settings** reads the Actors, packages and connections through `hub.*`, and the ChatGPT account through `login` when it's bound; the person is "you". An external Actor, the person included, shows whether it's connected; a package Actor its process state; a built-in one runs inside the Hub. **Packages** downloads each archive (`GET /packages/<hash>.agentorg`) and installs one from a file: uploaded to `POST /packages`, then `installer.plan` shows each slot's candidates (a slot's only one preselected), `installer.install` makes it with the person's picks (a connection the person can't grant is named, to leave unconnected), and its views can be placed on home; slots left unconnected and seeds that didn't land are listed. **System** exports the system as one file (`GET /system`), listing what it leaves out, and imports one (`POST /system`): what it made, what isn't as it was, and each management connection, for the person to grant one by one.
|
|
157
161
|
- **New agent** is offered once the first run has recorded the app's assistant, by hash, in the person's store (`app/first-run`), never a package found by its name. It is `installer.install` of that package with step 5's connections plus `logic.installer` to the installer, less the grants switched off, started, all or nothing; then it opens. The ChatGPT and installer it connects to are the ones the person's `login` and `installer` are bound to. `installAgent` in `@agentorg/shell/agent-bindings` makes that call, for a test too, and returns the installer's answer. "install Actors" needs "create and run Actors": with that off, it is off too and can't be switched on. A refusal is said plainly in the dialog, in room kept for it: a grant that isn't the person's to give, by name, or that AgentOrg isn't set up yet. A grant left on that the installer didn't connect, or a seed that didn't land, is listed before the agent opens.
|
package/dist/bundled/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"installer": "sha256-
|
|
3
|
-
"chatgpt": "sha256-
|
|
4
|
-
"assistant": "sha256-
|
|
5
|
-
"board": "sha256-
|
|
2
|
+
"installer": "sha256-4055f8fddf6b90f3d356543f554b2a9e9a2c4a8827a8774f8f87952dc1692331.agentorg",
|
|
3
|
+
"chatgpt": "sha256-e356ea3c1899138a8494baf521556831006f38534fa9e92ae873e7e6956c3a46.agentorg",
|
|
4
|
+
"assistant": "sha256-1f64ca62a0209d767d6aa6c4feb50b0653d60f10bebbc301f79bd3a37e0153c1.agentorg",
|
|
5
|
+
"board": "sha256-0e91cc3d0e9762b81abc2427b5496b20f383abcd6fb97d928a8d50e956b54d1f.agentorg"
|
|
6
6
|
}
|
|
Binary file
|
|
@@ -7,8 +7,8 @@ export declare const MAX_SOURCE: number;
|
|
|
7
7
|
export declare function checkSource(files: unknown): [string, string][];
|
|
8
8
|
/**
|
|
9
9
|
* Build a package from its source, given as data, and add it with `add`: the files are written to
|
|
10
|
-
* a fresh folder in the home, built as `agentorg build` builds (importing only the SDK,
|
|
11
|
-
* interfaces and
|
|
10
|
+
* a fresh folder in the home, built as `agentorg build` builds (importing by name only the SDK,
|
|
11
|
+
* the interfaces, zod, React, react-dom and the kit, and nothing outside the folder; reading no tsconfig; at most 16
|
|
12
12
|
* modules and 16 MiB built), and the folder is gone after, whatever happened. A build that fails
|
|
13
13
|
* is `Refused("build")` with the build's message, which names no path outside the package.
|
|
14
14
|
*/
|
|
@@ -8,8 +8,18 @@ import { BuildError, buildPackage } from "@agentorg/sdk/build";
|
|
|
8
8
|
export const MAX_SOURCE = 768 * 1024;
|
|
9
9
|
/** Files one package's source may have. */
|
|
10
10
|
const MAX_FILES = 256;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* What a package built by the Hub may import by name: the SDK, the interfaces, zod, and React and
|
|
13
|
+
* the kit for views (`react/jsx-runtime`, which JSX compiles to, is React's).
|
|
14
|
+
*/
|
|
15
|
+
const ALLOWED = [
|
|
16
|
+
"@agentorg/sdk",
|
|
17
|
+
"@agentorg/interfaces",
|
|
18
|
+
"zod",
|
|
19
|
+
"react",
|
|
20
|
+
"react-dom",
|
|
21
|
+
"@agentorg/kit",
|
|
22
|
+
];
|
|
13
23
|
/** Where those are found: this package's own folder (dist/src/build.js → the package). */
|
|
14
24
|
const FROM = fileURLToPath(new URL("../..", import.meta.url));
|
|
15
25
|
/** A source path: `package.json`, or a source file under `src/` named plainly. */
|
|
@@ -68,8 +78,8 @@ function told(message, source, home) {
|
|
|
68
78
|
}
|
|
69
79
|
/**
|
|
70
80
|
* Build a package from its source, given as data, and add it with `add`: the files are written to
|
|
71
|
-
* a fresh folder in the home, built as `agentorg build` builds (importing only the SDK,
|
|
72
|
-
* interfaces and
|
|
81
|
+
* a fresh folder in the home, built as `agentorg build` builds (importing by name only the SDK,
|
|
82
|
+
* the interfaces, zod, React, react-dom and the kit, and nothing outside the folder; reading no tsconfig; at most 16
|
|
73
83
|
* modules and 16 MiB built), and the folder is gone after, whatever happened. A build that fails
|
|
74
84
|
* is `Refused("build")` with the build's message, which names no path outside the package.
|
|
75
85
|
*/
|
|
@@ -85,6 +85,7 @@ export class HubActor {
|
|
|
85
85
|
...(Array.isArray(input.bindings)
|
|
86
86
|
? { bindings: input.bindings }
|
|
87
87
|
: {}),
|
|
88
|
+
...(typeof input.for === "string" ? { for: input.for } : {}),
|
|
88
89
|
}),
|
|
89
90
|
list: () => ({ actors: state.actors().map(recordOf) }),
|
|
90
91
|
get: (_, input) => recordOf(state.actor(String(input.actorId))),
|
|
@@ -115,6 +116,39 @@ export class HubActor {
|
|
|
115
116
|
await processes.stop(actorId);
|
|
116
117
|
state.closeActor(caller, actorId);
|
|
117
118
|
},
|
|
119
|
+
// A running Actor is stopped as `stop` does it, upgraded, and started again: on the new
|
|
120
|
+
// package, or on its own if the upgrade is refused. The machine's check comes first, so
|
|
121
|
+
// one that can't run it never has it stopped by an upgrade.
|
|
122
|
+
upgrade: async (caller, input) => {
|
|
123
|
+
const actorId = String(input.actorId);
|
|
124
|
+
const hash = String(input.package);
|
|
125
|
+
const actor = state.checkUpgrade(caller, actorId, hash);
|
|
126
|
+
if (actor.package === hash)
|
|
127
|
+
return { dropped: [] };
|
|
128
|
+
const running = actor.state === "running";
|
|
129
|
+
if (running) {
|
|
130
|
+
await processes.ready();
|
|
131
|
+
state.stop(caller, actorId);
|
|
132
|
+
await processes.stop(actorId);
|
|
133
|
+
}
|
|
134
|
+
const restart = () => {
|
|
135
|
+
// Started meanwhile, by another call: it runs already.
|
|
136
|
+
if (!running || state.actor(actorId).state === "running")
|
|
137
|
+
return;
|
|
138
|
+
state.start(caller, actorId);
|
|
139
|
+
processes.start(actorId);
|
|
140
|
+
};
|
|
141
|
+
let dropped;
|
|
142
|
+
try {
|
|
143
|
+
dropped = state.upgrade(caller, actorId, hash);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
restart();
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
restart();
|
|
150
|
+
return { dropped };
|
|
151
|
+
},
|
|
118
152
|
logs: (_, input) => {
|
|
119
153
|
// The Actor must exist; its lines are its process's output and its views' lines.
|
|
120
154
|
const { actorId } = state.actor(String(input.actorId));
|
|
@@ -125,6 +159,13 @@ export class HubActor {
|
|
|
125
159
|
grants: {
|
|
126
160
|
bind: (caller, input) => state.bind(caller, wiring(input)),
|
|
127
161
|
unbind: (caller, input) => state.unbind(caller, String(input.consumer), String(input.slot)),
|
|
162
|
+
expose: (caller, input) => state.expose(caller, {
|
|
163
|
+
provider: String(input.provider),
|
|
164
|
+
export: String(input.export),
|
|
165
|
+
to: String(input.to),
|
|
166
|
+
...(typeof input.as === "string" ? { as: input.as } : {}),
|
|
167
|
+
}),
|
|
168
|
+
unexpose: (caller, input) => state.unexpose(caller, String(input.to), String(input.slot)),
|
|
128
169
|
request: (caller, input) => ({
|
|
129
170
|
requestId: state.request(caller, wiring(input), String(input.reason)).requestId,
|
|
130
171
|
}),
|
|
@@ -2,5 +2,5 @@ export { HubActor } from "./hub-actor.js";
|
|
|
2
2
|
export { blockedAddresses, isPublicAddress, type NetOptions } from "./net.js";
|
|
3
3
|
export { type ChannelHandle, Hub, type HubOptions } from "./router.js";
|
|
4
4
|
export { type Listener, listen } from "./server.js";
|
|
5
|
-
export { type ActorRecord, type ActorState, type AuditRecord, type BindingRecord, type CallOutcome, type Decision, type Declaration, type Exit, HUB, HubState, NET, PERSON, type RequestRecord, type Side, STORAGE, type StateEvents, type Wiring, } from "./state.js";
|
|
5
|
+
export { type ActorRecord, type ActorState, type AuditRecord, type BindingRecord, type CallOutcome, type Decision, type Declaration, type Exit, exposedName, HUB, HubState, NET, PERSON, type RequestRecord, type Side, STORAGE, type StateEvents, type Wiring, } from "./state.js";
|
|
6
6
|
export { STORAGE_LIMITS, type StorageLimits } from "./storage.js";
|
|
@@ -2,5 +2,5 @@ export { HubActor } from "./hub-actor.js";
|
|
|
2
2
|
export { blockedAddresses, isPublicAddress } from "./net.js";
|
|
3
3
|
export { Hub } from "./router.js";
|
|
4
4
|
export { listen } from "./server.js";
|
|
5
|
-
export { HUB, HubState, NET, PERSON, STORAGE, } from "./state.js";
|
|
5
|
+
export { exposedName, HUB, HubState, NET, PERSON, STORAGE, } from "./state.js";
|
|
6
6
|
export { STORAGE_LIMITS } from "./storage.js";
|
|
@@ -191,7 +191,7 @@ export class Hub {
|
|
|
191
191
|
if (admitted) {
|
|
192
192
|
const actor = this.state.actor(admitted.actorId);
|
|
193
193
|
if (actor.package === "external" || actor.package === "builtin" || actor.state === "closed")
|
|
194
|
-
throw new Refused("state", `${actor.actorId} isn't a live package Actor.`);
|
|
194
|
+
throw new Refused("state", `${this.state.named(actor.actorId)} isn't a live package Actor.`);
|
|
195
195
|
}
|
|
196
196
|
let conn;
|
|
197
197
|
let admit;
|
|
@@ -216,11 +216,11 @@ export class Hub {
|
|
|
216
216
|
// Closed while its hello was on the way (a pipe made before a close, or declares
|
|
217
217
|
// being checked): a closed Actor is never admitted.
|
|
218
218
|
if (this.state.actor(actorId).state === "closed")
|
|
219
|
-
throw new Refused("state", `${actorId} is closed.`);
|
|
219
|
+
throw new Refused("state", `${this.state.named(actorId)} is closed for good; make another.`);
|
|
220
220
|
// Checked again as the channel is admitted: one may have connected while declares
|
|
221
221
|
// were checked.
|
|
222
222
|
if (replace === false && this.#connections.has(actorId))
|
|
223
|
-
throw new Refused("connected", `${actorId} already has a channel to this Hub.`);
|
|
223
|
+
throw new Refused("connected", `${this.state.named(actorId)} already has a channel to this Hub; close that one first.`);
|
|
224
224
|
conn = this.#admit(actorId, session);
|
|
225
225
|
admit();
|
|
226
226
|
options.maxStreams = MAX_STREAMS;
|
|
@@ -361,12 +361,12 @@ export class Hub {
|
|
|
361
361
|
#viewModule(actorId, module, options) {
|
|
362
362
|
const actor = this.state.actor(actorId);
|
|
363
363
|
if (actor.package === "external" || actor.package === "builtin")
|
|
364
|
-
throw new Refused("forbidden", `${actorId} has no package, so no views.`);
|
|
364
|
+
throw new Refused("forbidden", `${this.state.named(actorId)} has no package, so no views.`);
|
|
365
365
|
if (options.running && actor.state !== "running")
|
|
366
|
-
throw new Refused("state", `${actorId}
|
|
366
|
+
throw new Refused("state", `${this.state.named(actorId)} is ${actor.state}${actor.state === "closed" ? "" : "; start it with hub.actors.start"}.`);
|
|
367
367
|
const { manifest } = this.state.package(actor.package);
|
|
368
368
|
if (!Object.hasOwn(manifest.modules, module) || manifest.modules[module]?.placement !== "view")
|
|
369
|
-
throw new Refused("forbidden", `${module} isn't a view module of ${actorId}'s package.`);
|
|
369
|
+
throw new Refused("forbidden", `${module} isn't a view module of ${this.state.named(actorId)}'s package; hub.actors.get lists its views.`);
|
|
370
370
|
}
|
|
371
371
|
/**
|
|
372
372
|
* A line a view module logged, into its Actor's log, marked with the module. The Hub bounds it
|
|
@@ -438,7 +438,7 @@ export class Hub {
|
|
|
438
438
|
if (pipe) {
|
|
439
439
|
// The pipe is the proof: it was made for this one Actor's process.
|
|
440
440
|
if (header.actorId !== pipe.actorId)
|
|
441
|
-
throw new Refused("forbidden", `This channel is ${pipe.actorId}'s.`);
|
|
441
|
+
throw new Refused("forbidden", `This channel is ${this.state.named(pipe.actorId)}'s.`);
|
|
442
442
|
if (header.declares !== undefined)
|
|
443
443
|
throw new Refused("invalid", "A package Actor declares through its manifest.");
|
|
444
444
|
}
|
|
@@ -446,16 +446,19 @@ export class Hub {
|
|
|
446
446
|
!this.state.authenticate(header.actorId, header.credential))
|
|
447
447
|
throw new Refused("forbidden", "No such Actor, or the credential is wrong.");
|
|
448
448
|
if (header.replace === false && this.#connections.has(header.actorId))
|
|
449
|
-
throw new Refused("connected", `${header.actorId} already has a channel to this Hub.`);
|
|
449
|
+
throw new Refused("connected", `${this.state.named(header.actorId)} already has a channel to this Hub; close that one first.`);
|
|
450
450
|
if (header.declares !== undefined)
|
|
451
451
|
this.state.declareExternal(header.actorId, await this.#declares(header.actorId, header.declares));
|
|
452
452
|
admit(header.actorId, header.replace);
|
|
453
453
|
// Taken as the channel is admitted: every change after it reaches the channel as a notice.
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
.filter((b) => b.consumer === header.actorId)
|
|
457
|
-
.map((b) => b.slot);
|
|
454
|
+
const mine = this.state.bindings(header.actorId).filter((b) => b.consumer === header.actorId);
|
|
455
|
+
const bound = mine.map((b) => b.slot);
|
|
458
456
|
await answer(writer, result({ actorId: header.actorId, bound }));
|
|
457
|
+
// Each slot made at run time follows as its own notice, with its interface: one frame
|
|
458
|
+
// each, however many there are.
|
|
459
|
+
for (const b of mine)
|
|
460
|
+
if (b.exposed)
|
|
461
|
+
this.#notify(header.actorId, b.slot, true);
|
|
459
462
|
}
|
|
460
463
|
catch (error) {
|
|
461
464
|
await answer(writer, errorBody(error));
|
|
@@ -463,6 +466,13 @@ export class Hub {
|
|
|
463
466
|
}
|
|
464
467
|
}
|
|
465
468
|
/** Tell an Actor's channel and its views' listeners that one of its slots was bound or unbound. */
|
|
469
|
+
/** A slot made at run time, as its consumer is told of it: the export's interface, and whose it is. */
|
|
470
|
+
#exposure(binding) {
|
|
471
|
+
return {
|
|
472
|
+
interface: this.state.declaration(binding.consumer, "slot", binding.slot)?.iface,
|
|
473
|
+
provider: { actor: binding.provider, export: binding.export },
|
|
474
|
+
};
|
|
475
|
+
}
|
|
466
476
|
#notify(actorId, slot, bound) {
|
|
467
477
|
this.#notifyViews(actorId, slot, bound);
|
|
468
478
|
const conn = this.#connections.get(actorId);
|
|
@@ -470,12 +480,26 @@ export class Hub {
|
|
|
470
480
|
return;
|
|
471
481
|
if (conn.notices >= this.state.slotCount(actorId) + MAX_OPEN_NOTICES)
|
|
472
482
|
return conn.session.close(GO_AWAY.protocolError);
|
|
483
|
+
// A slot made at run time carries its exposure while bound; unbound, it is gone.
|
|
484
|
+
const binding = bound ? this.state.binding(actorId, slot) : undefined;
|
|
485
|
+
// Gone before it could be told: its unbinding is told instead.
|
|
486
|
+
if (bound && !binding)
|
|
487
|
+
return;
|
|
488
|
+
const exposed = binding?.exposed ? { exposed: this.#exposure(binding) } : {};
|
|
489
|
+
let frame;
|
|
490
|
+
try {
|
|
491
|
+
frame = encodeFrame({ op: "slot", name: slot, bound, ...exposed });
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// An interface offered in one frame fits one; nothing else here can be too large.
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
473
497
|
try {
|
|
474
498
|
const stream = conn.session.open();
|
|
475
499
|
conn.notices++;
|
|
476
500
|
const writer = stream.writable.getWriter();
|
|
477
501
|
void writer
|
|
478
|
-
.write(
|
|
502
|
+
.write(frame)
|
|
479
503
|
.then(() => writer.close())
|
|
480
504
|
.catch(() => { });
|
|
481
505
|
// Nothing comes back; the Actor closing its side ends the read and frees the notice.
|
|
@@ -657,12 +681,12 @@ export class Hub {
|
|
|
657
681
|
if (method !== "seed")
|
|
658
682
|
throw new Refused("unimplemented", `storage.admin@1 has no method ${method}.`);
|
|
659
683
|
if (this.state.actor(input.consumer).state === "closed")
|
|
660
|
-
throw new Refused("state", `${input.consumer} is closed.`);
|
|
684
|
+
throw new Refused("state", `${this.state.named(input.consumer)} is closed for good.`);
|
|
661
685
|
// Only a store the consumer can reach: its slot, bound to storage's kv.
|
|
662
686
|
const bound = this.state.binding(input.consumer, input.slot);
|
|
663
687
|
if (bound?.provider !== STORAGE || bound.export !== "kv")
|
|
664
|
-
throw new Refused("invalid", `${input.consumer}
|
|
665
|
-
this.#storage.seed(input.consumer, input.slot, input.entries);
|
|
688
|
+
throw new Refused("invalid", `${this.state.named(input.consumer)}'s ${input.slot} isn't a slot bound to storage.kv; bind it there first.`);
|
|
689
|
+
this.#storage.seed(input.consumer, input.slot, input.entries, this.state.named(input.consumer));
|
|
666
690
|
// The seed is done; an audit that fails now (the Hub closing) doesn't undo or hide it.
|
|
667
691
|
try {
|
|
668
692
|
this.state.auditHub("storage.seed", { consumer: input.consumer, slot: input.slot, entries: input.entries.length }, caller);
|